/**************************************************************************
 * @name: img_show.c
 * @brief: 读取指定图片
 * @date: 2025/01/08
 * @author: boluzs@163.com
 * @note: None
 * @version: 1.0.0
 * @Copyright (c) 2025 by boluzs@163.com, All Rights Reserved.
 **************************************************************************/
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <unistd.h>
#include <fcntl.h>

#define LCD_DEV "/dev/fb0"
#define LCD_WIDTH 800
#define LCD_HEIGHT 480
#define LCD_SIZE 800 * 480 * 4
#define BMP_LAST_BYTE(img_width) ((4 - ((img_width) * 3) % 4) % 4)

typedef struct BmpInfo
{
    unsigned int width;
    unsigned int height;
} BmpInfo_t, *BmpInfo_p;

int main(int argc, char const *argv[])
{
    // 1.判断参数是否有误
    if (argc != 2)
    {
        fprintf(stderr, "open error,errno = %d,%s\n", errno, strerror(errno));
        exit(-1);
    }

    // 2.读取图片和lcd文件
    FILE *bmp = fopen(argv[1], "rb");
    int lcd_fd = open(LCD_DEV, O_RDWR);

    // 3.读取图片的大小以及宽高
    BmpInfo_t bmp_info;
    fseek(bmp, 18, SEEK_SET);
    fread(&bmp_info.width, 4, 1, bmp);
    fread(&bmp_info.height, 4, 1, bmp);
    fseek(bmp, 54, SEEK_SET);

    // 4.映射lcd文件到内存
    int *lcd_fp = (int *)mmap(NULL, LCD_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED,
                              lcd_fd, 0);
    if (lcd_fp == MAP_FAILED)
    {
        fprintf(stderr, "mmap lcd error,errno = %d,%s\n", errno, strerror(errno));
        exit(-1);
    }

    // 5.以行为单位循环读取BMP图片的颜色分量，并刷新到LCD
    char buf_line[bmp_info.width * 3];
    for (int y = 0; y < bmp_info.height; y++)
    {
        fread(buf_line, bmp_info.width * 3, 1, bmp);
        // 6.对每行不是4的倍数进行字节补齐
        fseek(bmp, BMP_LAST_BYTE(bmp_info.width), SEEK_CUR);
        for (int x = 0; x < bmp_info.width; x++)
        {
            // 7.BMP是小端存储，所以3个字节的颜色分量的顺序是BGR
            *(lcd_fp + LCD_WIDTH * (LCD_HEIGHT - y - 1) + x) = buf_line[x * 3] + ((buf_line[x * 3 + 1]) << 8) + ((buf_line[x * 3 + 2]) << 16);
        }
    }

    // 关闭文件和映射
    fclose(bmp);
    close(lcd_fd);
    munmap(lcd_fp, LCD_SIZE);
    return 0;
}
